
var https = require("https")
var fs = require("fs")
var tsl = {
  cert: fs.readFileSync("/root/.acme.sh/jsfeng.info/jsfeng.info.cer"),
  key: fs.readFileSync("/root/.acme.sh/jsfeng.info/jsfeng.info.key"),
}


var http = require("http")
const redirecd = function (req, res) {
  res.writeHead(301, {"Location": "https://jsfeng.info"})
  console.log(res._header)
  res.end()
}

const postNumber = 443
const express = require("express")
const path = require("path")
const bodyParser = require("body-parser")
const sqlite = require("sqlite")
const cookieParser = require("cookie-parser")
const multer = require("multer")
const svgCaptcha = require('svg-captcha')


const upload = multer({dest:"./user-uploaded"})

const app = express()
app.use(cookieParser("adffv"))
//这个用来给cookie加密和解密
const dbPromise = sqlite.open("./data/bbs.db")
var db
var sessions={}
var changePasswordLink={}

app.locals.pretty = true
app.set("views","./templates")
//写了这一行以后，下面的模版文件只需要写名字都行了，因为他们都是在这个文件夹下找的
app.set('view engine', 'pug')
//这行的意思是如果render里面的文件没有扩展名，则会自动加上设置的这个后缀


app.use(express.static('public'))
//静态文件直接去public文件夹下面寻找
//如果直接访问。貌似默认是打开index.html,如果找不到index.html再走下面的中间件
app.use("/avatar",express.static("user-uploaded"))
app.use(bodyParser.urlencoded())
app.use(bodyParser.json())

//经过这个中间件以后，req的请求体就放在req的body字段上面，主要是拿来获取post请求的请求体
//使用urlencoded函数是因为表单数据默认是用url编码以后再传递的
app.use(async(req,res,next)=>{
  if(req.signedCookies){
    //如果有signedCookies这个属性那么就说明有加密过的cookie传过来
    req.user = await db.get("select * from users where id=?",req.signedCookies.userId)
  }
  next()
})

app.get("/api/userInfo",(req,res)=>{
  res.json(req.user)
})

app.get("/api/index",async (req,res,next)=>{
  let posts = await db.all("select * from posts")
  posts.forEach(async (post,idx)=>{
    let comments = await db.all("select * from comments where comments.postId=?",post.id)
    //console.log("-------评论分割线",comments)
    post.commentLength = comments.length
    post.avatar = []
    //下面地方有问题，要发送所有用户的头像，包括作者和回复者
    let user = await db.get("select * from users where users.id=?",post.userId)
    //发帖作者
    post.avatar.push(user.avatar)
    // if(idx == posts.length - 1 && comments.length == 0){
    //   console.log("发送数据！",new Date())
    //   res.json(posts)
    // } 
    
    comments.forEach(async (comment,commentIdx) =>{
      let user = await db.get("select * from users where users.id=?",comment.userId)
      
      if(!post.avatar.includes(user.avatar)){
        console.log("添加进去avatar",new Date())
        post.avatar.push(user.avatar)
      }              
      // if(idx == posts.length - 1 && commentIdx == comments.length - 1 ){
      //   console.log("发送数据？",new Date())
      //   res.json(posts)
      // }
    })    
  })
  //这里需要修改，暂时用settimeout耍下。。
  setTimeout(()=>{
    res.json(posts)
    console.log("发送数据",new Date())
  },50)
    
})


app.post("/api/login",async (req,res)=>{
  
  if(sessions[req.cookies.sessionId]==req.body.captcha){
    let user = await db.get("select * from users where name=?",req.body.username)
    if(user && user.password == req.body.password){
      res.cookie("userId",user.id,{
        signed: true,
        // httpOnly: true,
      })
      res.status(200).json({
        user
      })
    } else{
      res.status(403).end("username or password is not correct")
      //res.end请求会导致axios的请求返回一个reject的promise
    }
  } else{
    res.status(403).end("captcha error")
  }
})

app.post("/api/register",upload.single('avatar'),async (req,res)=>{

  let user = await db.get("select * from users where name = ?",req.body.username)
  if(user){
    res.status(403).end()
  }else{
    await db.run("insert into users(name,password,avatar) values(?,?,?)",req.body.username,req.body.password,req.file.filename)
    res.json("注册成功")
  }
})

app.post("/api/addPost",async (req,res)=>{
  if(req.signedCookies && req.signedCookies.userId){
    await db.run("insert into posts(userId,title,content,timestamp) values(?,?,?,?)",req.signedCookies.userId,req.body.title,req.body.content,Date.now())
    let post = await db.get("select * from posts order by timestamp desc limit 1")    
    // res.redirect("/posts/"+ post.id)
    res.status(200).json({
      post
    })  
    
  } else{
    res.status(403).end("you have not login")
  }

})

app.get("/api/deletePost/:postId",async (req,res)=>{

  if(req.user){
    let post = await db.get("select * from posts where id=?",req.params.postId)
    if(post.userId == req.user.id){
      await db.run("delete from posts where id=?",post.id)
      res.status(200).end("ok")
    }
  }else{
    req.status(403).end()
  }
})

app.post("/api/addComment",async (req,res)=>{
  if(req.signedCookies && req.signedCookies.userId){
    await db.run("insert into comments(postId,userId,content,timestamp) values(?,?,?,?)",req.body.postId,req.signedCookies.userId,req.body.newComment,Date.now())
    let comment = await await db.get("select * from comments order by timestamp desc limit 1")
    res.status(200).end("ok")
  }else{ 
    res.status(403).end("not allowed to comment,you have not login")
  }
})

app.get("/api/posts/:postId",async (req,res)=>{
  //需要获取的数据：1.帖子作者名字，头像，标题，内容，2 帖子评论者名字 头像，评论内容 3.帖子作者的所有帖子
  let post = await db.get("select posts.*,avatar,name from posts join users on posts.userId=users.id where posts.id=?",req.params.postId)
  let posts = await db.all("select * from posts where posts.userId=?",post.userId)
  let comments = await db.all("select comments.*,name,avatar from comments join users on comments.userId=users.id where comments.postId=?",req.params.postId)
  res.status(200).json({
    post,comments,posts
  })

})

app.get("/api/users/:userId",async (req,res)=>{
  if(req.user){
    let userPosts = await db.all("select * from posts where userId=?",req.params.userId)
    res.status(200).json({user:req.user,posts:userPosts})
  }else{
    res.status(403).end("you have not login")
    //通过在模版里面写if和else，可以避免多写一个模版
  }
})




app.use(function sessionMiddleware(req,res,next){
  if(!req.cookies.sessionId){
    res.cookie("sessionId",Math.random().toString(16).substr(2))
  }
  next()
})

app.get("/",async (req,res,next)=>{
  let posts = await db.all("select * from posts")
  res.render("index.pug",{posts,user:req.user})
})

app.get("/captcha",(req,res)=>{
  let captcha = svgCaptcha.create()
  sessions[req.cookies.sessionId] = captcha.text

  res.type('svg')
  res.status(200).send(captcha.data)
})
  
app.route("/register")
  .get(function(req,res){
    res.render("register.pug",{user:req.user})
  })
  .post(upload.single('avatar'),async(req,res)=>{  
    let user = await db.get("select * from users where name = ?",req.body.username) 
    
    if(user){
      res.end("该名字已经被占领了")
    }else{
      await db.run("insert into users(name,password,avatar) values(?,?,?)",req.body.username,req.body.password,req.file.filename)
      res.redirect("/login")
    }  
       
  }) 

app.get("/logout",(req,res,next)=>{
  console.log("准备登出")
  res.clearCookie("userId")
  res.end()
  
})  

app.route("/login")
  .get((req,res)=>{
    res.render("login.pug",{user:req.user})
  })
  .post(async(req,res)=>{  
    
    console.log(req.body)  
    if(sessions[req.cookies.sessionId]==req.body.captcha){
      let user = await db.get("select * from users where name=?",req.body.username)
      if(user && user.password == req.body.password){
        res.cookie("userId",user.id,{
          signed: true,
          // httpOnly: true,
        })
        res.redirect("/")
      } else{
        res.status(403).end("用户名或密码错误")
        //res.end请求会导致axios的请求返回一个reject的promise
      }
    } else{
      res.status(403).end("captcha error")
    }
    
    
  })


app.route("/add-post")
  .get((req,res,next)=>{
    res.render("add-post.pug",{user:req.user})
  })
  .post(async (req,res,next)=>{
    if(req.signedCookies && req.signedCookies.userId){
      await db.run("insert into posts(userId,title,content,timestamp) values(?,?,?,?)",req.signedCookies.userId,req.body.title,req.body.content,Date.now())
      let post = await db.get("select * from posts order by timestamp desc limit 1")
      res.redirect("/posts/"+ post.id)
    } else{
      res.end("you have not login")
    }
  })

app.get("/delete-post/:postId",async (req,res)=>{
  let post = await db.get("select * from posts where id=?",req.params.postId)
  if(post.userId == req.user.id){
    await db.run("delete from posts where id=?",post.id)
    
  }
  res.redirect("/users/"+post.userId)
})

app.get("/posts/:postId",async(req,res,next)=>{
  let post = await db.get("select posts.*,avatar,name from posts join users on posts.userId=users.id where posts.id=?",req.params.postId)
  if(post){
    let comment = await db.all("select comments.*,name,avatar from comments join users on comments.userId=users.id where comments.postId=?",req.params.postId)
    res.render("post.pug",{post,comment,user:req.user})
  }else {
    res.status(404).render("post-not-found.pug")
  }
})
//使用这个带：的路径地址之后相当于冒号后的属性存在req.params上面
app.get("/users/:userId",async (req,res,next)=>{
  if(req.user){
    let userPosts = await db.all("select * from posts where userId=?",req.params.userId)
    res.render("user.pug",{user:req.user,posts:userPosts})
  }else{
    res.render("user.pug",{user:null})
    //通过在模版里面写if和else，可以避免多写一个模版
  }
})

app.post("/add-comment",async (req,res,next)=>{
  if(req.signedCookies && req.signedCookies.userId){
    await db.run("insert into comments(postId,userId,content) values(?,?,?)",req.body.postId,req.signedCookies.userId,req.body.content)
    res.redirect("/posts/" + req.body.postId)
  }else{ 
    res.end("not allowed to comment,you have not login")
  }
  
})


;
(async function(){
  db = await dbPromise
  https.createServer(tsl,app).listen(postNumber,()=>{
    console.log("mySever by htps loding...",postNumber)
  })

  http.createServer(redirecd).listen(80,()=>{
      console.log("mySever by http loding...",80)
    })

  // http.createServer(app).listen(9527,()=>{
  //   console.log("mySever by http loding...",8080)
  // })

  // app.listen(10086,()=>{
  //   console.log("listening at 10086")
  // })
})()


